<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>ECMA测试</title>
</head>

<body>
  <script>
    //  1.  Javascript 的数据类型有几种？分别又有哪些类型？（5）
    /* 
      答：8种
          基本（原始）数据类型：Number、String、Boolean、Undefined、Null、Symbol、BigInt
          引用（复杂）数据类型：Object、（Array）

    */

    //  2.  return 有那两个作用？ 构造函数里return会怎么样？（5）
    /* 
      答：作用：
          1、返回一个值
          2、终止函数的执行
          构造函数的return如果返回一个非空对象或者是基本数据类型，则会返回当前创建的新对象，反之，则返回一个空对象
    */

    //  3.  undefined 和 null 的区别？什么时候会得到undefined？（5）
    /* 
      答：
        undefined：是一个基本数据类型，表示一个变量声明了，但是未赋值。
        null:是Object中的一个特殊类型，用于初始化对象。
        1、已声明但未被初始化的变量默认赋值undefined
        2、未被声明的变量使用typeof操作符检测数据类型时返回undefined
        3、当函数没有明确指定返回值时，默认返回undefined
        4、viod()返回undefined

    */

    //  4.  使用 continue 或 break 终止循环时，两种方式的区别是？（5）
    /* 
      答：
        continue：continue是跳过本次的循环，然后继续执行下次循环。
        break：break是直接终止整个循环。
    */

    //  5.  如何判断字符串内是否包含指定字符？（5）
    /* 
      答：
        indexOf(指定字符)：若包含该字符，return该字符的下标，反之，return -1
        includes(指定字符):return布尔值，true表示包含该字符，false表示不包含该字符
    */

    //  6. 值传递和引用传递的区别？ （5）
    /* 
      答：
        值传递传递的是值，相当于复制了一份，原本的值不会因为其他的修改而改变。
        引用传递传递的是引用的地址，相当于指向了同一个引用地址，如果改变另一个，原来的那个也会改变。

        * 值类型(原始类型):在变量中保存的是值本身,占据的空间是在栈内存中分配的
        * 引用类型(对象类型):在变量中保存的是对象的“引用”,占据的空间是在堆内存中分配的
        1)将值类型传递给函数参数,函数内部对参数的改变不会影响函数外部的变量
        2)将引用类型传递给函数参数,函数参数保存的是对象的"引用",在函数内部修改对象的属性会影响函数外部的变量

    */

    //  7. 请尽量列举出数组的方法,并注明其作用以及会不会修改原数组，至少10种（10）
    /* 
      答：
      push()：将一个或多个元素添加到数组的尾部，返回新的长度，会改变原数组。
      pop():从数组的尾部删除一个元素，一次只能删除一个，返回被删除的那个元素，会改变原数组。
      unshift():将一个或多个元素添加到数组的头部，返回新的长度，会改变原数组。
      shiift():从数组的头部删除一个元素，一次只能删除一个，返回被删除的那个元素，会改变原数组。
      splice():插入、替换、删除数组中的元素，会修改原数组。
      concat():用于合并多个数组。返回一个新的数组。
      indexOf():用于查找指定的字符，若找到，返回该字符的下标，反之，返回 -1，不会改变原数组。
      join():将数组里的元素按顺序连接起来并以字符串的形式返回，会改变原数组。
      reverse():将数组里的元素反转(从后往前)排列，返回一个新的数组，不会改变原数组。
      filter():将数组中符合函数条件的元素筛选出来，返回一个新的数组，不会改变原数组。
    */

    //  8.  请写出以下输出的结果和原因；（10）
    var bar = true,
      number = 0;
    console.log(bar + 0); //1;当布尔类型的true和数值类型的0进行加法运算时，布尔类型的true会隐式转化为数值类型的1参与运算，则1+0=1
    console.log(bar + 'xyz'); //truexyz；当布尔类型true与字符串参与加法运算时，布尔类型的true会隐式转化为字符串类型的'true'参与运算，则'true'+'xyz'=truexyz
    console.log(bar + true);  //2;当两个布尔类型的true进行加法运算时，布尔类型的true会隐式转化数值类型的1参与运算，则1+1=2；
    console.log(+true); //1；当布尔类型的true进行一元运算时，布尔类型的true会隐式转化数值类型的1参与运算，则+1=1；
    console.log(!'bluej');  //false；取反！运算时会把非布尔类型运算元隐式转换成布尔类型，然后取反，而非空的字符串的布尔类型为true，则取反后为false
    console.log(number++);  //0；后置++，先运算，后自增+1，则先输出原来的值0，之后number=1
    console.log(++number);  //2；前置++。之前number=1，先自增+1，则number=2，再运算。
    console.log(number);  //2，最后的number还是2；
    10 > 20 || console.log('haha'); //haha；逻辑运算符 || 运算时先隐式转换成布尔类型进行判断，第一个布尔值为false，则继续进行第二个操作数的运算，结果也是为false，所以最后输出第二个操作数的初始值haha。
    10 > 20 && console.log('haha'); //什么也不会输出；因为逻辑运算符 && 的短路运算，第一个操作数的结果为false，所以后面的代码不会执行，返回第一个操作数的初始值，由于打印输出，所以什么也没有。

    //  9. 将多个数组进合并后再去重；对于去重的方案要求3种及以上（10）
    var arr1 = [1, 2, 3],
      arr2 = [2, 3, 4, 5];

    // 连接后的数字
    var concatArr = arr1.concat(arr2);
    // console.log(concatArr);
    // 方法一：先排序，再比较前一个与一个是否重复，重复删掉当前那个，继续比较
    function fn1() {
      concatArr.sort((a, b) => a - b);
      // console.log(concatArr);
      for (var i = 0; i < concatArr.length; i++) {
        if (concatArr[i] === concatArr[i + 1]) {
          concatArr.splice(i, 1);
          i--;
        }
      }
      console.log(concatArr);
    }
    fn1();

    // 方法二：将新数组里没有的添加到新数组中，重复的不添加
    function fn2() {
      var newArr = [];
      for (var i = 0; i < concatArr.length; i++) {
        if (newArr.indexOf(concatArr[i]) === -1) {
          newArr.push(concatArr[i]);
        }
      }
      console.log(newArr);
    }
    fn2();

    // 方法三：根据Set的不重复性
    function fn3() {
      var newArr = [...new Set(concatArr)]
      console.log(newArr);
    }
    fn3();

    //方法四:先转化对象，然后再存进数组
    function fn4() {
      var obj = concatArr.reduce((prev, item) => {
        prev[item] = "去重";
        return prev;
      }, {})
      // console.log(obj);

      var newArr = []
      for (var key in obj) {
        newArr.push(key - 0);
      }
      console.log(newArr);
    }
    fn4();

    // 方法五：冒泡去重
    function fn5() {
      for (var i = 0; i < concatArr.length; i++) {
        var index = concatArr.indexOf(concatArr[i], i + 1);
        if (index !== -1) {
          concatArr.splice(index, 1);
        }
      }
      console.log(concatArr);
    }
    fn5();


    //10.将 str="再见，还会再见吗燕子，再见的时候你要幸福好不好，燕子你要开心，你要幸福，好不好，开心啊，幸福！你的世界以后没有我了，没关系你要幸福，🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊燕子，燕子，燕子，燕子，燕子，燕子，燕子你把我带走吧，燕子！"里面的所有"燕子"替换成"jack"不要用replaceAll(10)

    function replaceStr() {
      var str = "再见，还会再见吗燕子，再见的时候你要幸福好不好，燕子你要开心，你要幸福，好不好，开心啊，幸福！你的世界以后没有我了，没关系你要幸福，🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊🚗~~ 🏃🏻~~燕子 🧎🏻燕子~~没有你我怎么活啊燕子，燕子，燕子，燕子，燕子，燕子，燕子你把我带走吧，燕子！"
      while (str.indexOf("燕子") !== -1) {
        str = str.replace("燕子", "jack")
      }
      console.log(str);
    }
    replaceStr();

    //11. 找出数组中年龄小于18岁的女生(girl)的信息重新组成新的数组,并且将她们count变为现有的平方（10）
    var arr3 = [
      { name: '彭于晏', sex: 'boy', age: 18, count: 20 },
      { name: '迪丽热巴', sex: 'girl', age: 17, count: 30 },
      { name: '吴彦祖', sex: 'boy', age: 16, count: 40 },
      { name: '古力娜扎', sex: 'girl', age: 15, count: 50 },
      { name: '刘德华', sex: 'boy', age: 20, count: 60 },
      { name: '刘亦菲', sex: 'girl', age: 13, count: 70 },
      { name: '张学友', sex: 'boy', age: 14, count: 80 },
    ];

    function getArrMinAge() {
      var newArr3 = [];
      arr3.map((item) => {
        if (item.sex === "girl" && item.age < 18) {
          item.count **= 2;
          newArr3.push(item);
        }
      })
      console.log(newArr3);
    }
    getArrMinAge()
    // 12. 统计字符串'abbcccddddd'中每个字符出现的次数，统计数组['a','b','b','c','c','c','d','d','d','d','d'中每个字符出现的次数(10分)
    function getEveryStrNum() {
      var str = "abbcccddddd";
      // 先转成数组，然后使用reduce()
      var obj = str.split("").reduce((prev, item) => {
        prev[item] = prev[item] + 1 || 1;
        return prev
      }, {})
      console.log(obj);
      for (var key in obj) {
        console.log(`${key} 字符出现了${obj[key]}次`);
      }
    }
    getEveryStrNum();
  </script>
</body>

</html>